﻿using System.Web;
using System.Text;
using System.IO;
using System;
using System.Collections.Generic;
using System.Security.Cryptography.X509Certificates;
using Com.Itrus.Cvm;
using Com.Itrus.Cert;
using Com.Itrus.Util;
using Com.Itrus.Svm;
using Com.Itrus.CryptoRole;
using System.Collections;
using BaifudataPay.Applib;

namespace Com.Allscore
{
    /// <summary>
    /// 类名：Core
    /// 功能：商银信接口公用函数类
    /// 详细：该类是请求、通知返回两个文件所调用的公用函数核心处理文件，不需要修改
    /// 版本：1.0
    /// 修改日期：2011-12-20
    /// 说明：
    /// 以下代码只是为了方便商户测试而提供的样例代码，商户可以根据自己网站的需要，按照技术文档编写,并非一定要使用该代码。
    /// 该代码仅供学习和研究商银信接口使用，只是提供一个参考。
    /// </summary>
    public class Core
    {
        public Core()
        {
        }

        /// <summary>
        /// 生成签名结果
        /// </summary>
        /// <param name="sArray">要签名的数组</param>
        /// <param name="_input_charset">编码格式</param>
        /// <returns>签名结果字符串</returns>
        public static string BuildMysignCA(Dictionary<string, string> dicArray, string _input_charset)
        {
            string prestr = CreateLinkString(dicArray);  //把数组所有元素，按照“参数=参数值”的模式用“&”字符拼接成字符串

            String mysign = "";


            mysign = SignCA(prestr);


            return mysign;
        }

        /// <summary>
        /// 除去数组中的空值和签名参数并以字母a到z的顺序排序
        /// </summary>
        /// <param name="dicArrayPre">过滤前的参数组</param>
        /// <returns>过滤后的参数组</returns>
        public static Dictionary<string, string> FilterPara(SortedDictionary<string, string> dicArrayPre)
        {
            Dictionary<string, string> dicArray = new Dictionary<string, string>();
            foreach (KeyValuePair<string, string> temp in dicArrayPre)
            {
                if (temp.Key.ToLower() != "sign" && temp.Key.ToLower() != "signtype" && temp.Value != "" && temp.Value != null)
                {
                    dicArray.Add(temp.Key, temp.Value);
                }
            }

            return dicArray;
        }

        /// <summary>
        /// 把数组所有元素，按照“参数=参数值”的模式用“&”字符拼接成字符串
        /// </summary>
        /// <param name="sArray">需要拼接的数组</param>
        /// <returns>拼接完成以后的字符串</returns>
        public static string CreateLinkString(Dictionary<string, string> dicArray)
        {
            StringBuilder prestr = new StringBuilder();
            foreach (KeyValuePair<string, string> temp in dicArray)
            {
                prestr.Append(temp.Key + "=" + temp.Value + "&");
                //prestr.Append(temp.Key + "=" + HttpUtility.UrlEncode(temp.Value,Encoding.UTF8) + "&");
            }

            //去掉最后一个&字符
            int nLen = prestr.Length;
            prestr.Remove(nLen-1,1);

            return prestr.ToString();
        }





        //签名测试方法
        //toSignStr 签名原文
        public static string SignCA(string toSignStr)
        {
            //构造签名实现类实例
            Sender s = new SenderDotNet2Impl();
            //初始化，传入PFX证书文件路径 string 类型 和证书私钥保护密码 char[]类型 	
            s.InitCertWithKey("D://test//co_password.pfx", "password".ToCharArray());
            //原文字符串转换byte[]  注意签名和验签时处理中文编码一致
            byte[] toSignBuf = Encoding.UTF8.GetBytes(toSignStr);
            //执行签名
            byte[] signedDataBuf = s.SignMessage(toSignBuf);
            //签名结果转换BASE64格式
            String signedDataB64 = Convert.ToBase64String(signedDataBuf);
            return signedDataB64;
        }

        //CVM模块用来验证证书有效性，全局唯一实例，只需要初始化一次，最好在系统启动时进行初始化
        //cvmpath cvm.xml文件路径 与cvm.xml统计目录中要有cafiles文件夹配置相应的中级证书和crl
        public void initCVM(string cvmpath)
        {
            CVM cvm = CVM.GetInstance();
            cvm.Config(cvmpath);
        }

        //验签测试方法
        //toSignStr
        //signedDataB64
        public static bool VerifyCA(string toSignStr, string signedDataB64)
        {
           

            signedDataB64 = signedDataB64.Replace("\\+", "*");
            signedDataB64 = signedDataB64.Replace("/", "-");

            //验签的原文转换byte[] 注意和签名时编码一致，该事例为UTF-8编码
            byte[] toSignBuf = Encoding.UTF8.GetBytes(toSignStr);
            //把签名结果的base64编码转换成byte[]
            byte[] signedDataBuf = Convert.FromBase64String(signedDataB64);
            //调用SVM 执行验签方法，如果验签成功返回X509Certificate证书对象，否则返回异常
            X509Certificate cert = SVM.VerifySignature(toSignBuf, signedDataBuf);
            //调用CVM模块验证证书有效性，如果i为0 则证书有效，验证通过，其他值验证失败。
            CVM cvm = CVM.GetInstance();
            string configFileName = AppDomain.CurrentDomain.BaseDirectory+("Bin/cvm.xml");
            cvm.Config(configFileName);
            
            int i = (int)cvm.VerifyCertificate(cert);

            CertificateStatus ret1 = cvm.VerifyCertificate(cert);
            HttpContext.Current.Response.Write("证书验证结果，Return=[" + ret1 + "]，");
            if (ret1 != CertificateStatus.VALID)
            {
                switch (ret1)
                {
                    case CertificateStatus.CVM_INIT_ERROR:
                        HttpContext.Current.Response.Write("CVM初始化错误，请检查配置文件或给CVM增加支持的CA。");
                        break;
                    case CertificateStatus.CRL_UNAVAILABLE:
                        HttpContext.Current.Response.Write("CRL不可用，未知状态。");
                        break;
                    case CertificateStatus.EXPIRED:
                        HttpContext.Current.Response.Write("证书已过期。");
                        break;
                    case CertificateStatus.ILLEGAL_ISSUER:
                        HttpContext.Current.Response.Write("非法颁发者。");
                        break;
                    case CertificateStatus.REVOKED:
                        HttpContext.Current.Response.Write("证书已吊销。");
                        break;
                    case CertificateStatus.UNKNOWN_ISSUER:
                        HttpContext.Current.Response.Write("不支持的颁发者。<font color=red>请检查cvm.xml配置文件</font>");
                        break;
                    case CertificateStatus.REVOKED_AND_EXPIRED:
                        HttpContext.Current.Response.Write("证书被吊销且已过期。");
                        break;
                }
                
            }
            HttpContext.Current.Response.Write("证书状态有效。");

            Names certNames = CertUtils.GetSubjectNames(cert);
            string commonName = null;
            string merchantId = "";

            commonName = certNames.GetItem("CN");
            HttpContext.Current.Response.Write("<h3>取证书主题单项，CN=[" + commonName + "]</h3>");
            HttpContext.Current.Response.Write("证书主题分解如下：<ul>");
            foreach (string key in certNames.Keys)
            {
                ArrayList values = certNames.GetItems(key);
                for (int j = 0; j < values.Count; j++)
                {
                    HttpContext.Current.Response.Write("<li>" + key + "=[" + values[j] + "]</li>");

                    if (values[j].ToString().Contains("UID"))
                    {
                        merchantId=(string)values[j];
                    }
                }
            }
            HttpContext.Current.Response.Write("</ul>");

            //获取商户号
            merchantId = merchantId.Substring(4, 15);
           


            // 判断证书状态及是否为商银信商户的证书
            if (i == (int)CertificateStatus.VALID && merchantId == Config.allscoreMerchantId)
                return true;
            else
                return false;
        }

        //验签测试方法
        //toSignStr
        //signedData
        public static bool VerifyMD5(string toSignStr, string signedData)
        {
            string key = Config.md5Key;

            string contentSignStr =  MD5Util.sign(toSignStr, key, "UTF-8");

            // 判断两边签名的数据是否一致
            if (contentSignStr == signedData)
                return true;
            else
                return false;
        }

        /// 生成签名结果
        /// </summary>
        /// <param name="sArray">要签名的数组</param>
        /// <param name="_input_charset">编码格式</param>
        /// <returns>签名结果字符串</returns>
        public static string BuildMysignRSA(Dictionary<string, string> dicArray, string _input_charset)
        {
            string prestr = CreateLinkString(dicArray);  //把数组所有元素，按照“参数=参数值”的模式用“&”字符拼接成字符串

            String mysign = "";

            mysign = RSAUtil.sign(prestr, Config.PrivateKey, _input_charset);

            return mysign;
        }
        /// 生成签名结果
        /// </summary>
        /// <param name="sArray">要签名的数组</param>
        /// <param name="_input_charset">编码格式</param>
        /// <returns>签名结果字符串</returns>
        public static string BuildMysignMD5(Dictionary<string, string> dicArray, string _input_charset)
        {
            string prestr = CreateLinkString(dicArray);  //把数组所有元素，按照“参数=参数值”的模式用“&”字符拼接成字符串

            String mysign = "";

            mysign = MD5Util.sign(prestr, Config.md5Key, _input_charset);

            return mysign;
        }

        

        //验签测试方法
        //toSignStr
        //signedDataB64
        public static bool VerifyRSA(string toSignStr, string signedDataB64, string input_charset)
        {
            bool result = false;
            string publicKey = Config.PublicKey;

            result = RSAUtil.verify(toSignStr, signedDataB64, publicKey, input_charset);

            return result;
        }


        public static void LogResult(string sWord)
        {
            LogUtil.Instance.WriteInfo(sWord);
        }


        /// <summary>
        /// 写日志，方便测试（看网站需求，也可以改成把记录存入数据库）
        /// </summary>
        /// <param name="sWord">要写入日志里的文本内容</param>
        public static void LogResult2(string sWord)
        {
            string strPath = HttpContext.Current.Server.MapPath(string.Format("/Runtime/log/allscore/"));
            HttpContext.Current.Response.Write(strPath);
            if (!Directory.Exists(strPath))
            {
                try
                {
                    Directory.CreateDirectory(strPath);
                }
                catch (Exception ex)
                {
                    throw ex;
                }
            }
            strPath = strPath + DateTime.Now.ToString("yyyyMMdd_HH") + ".txt";
            //strPath = "C://log.txt";
            StreamWriter fs = new StreamWriter(strPath, false, System.Text.Encoding.Default);
            fs.Write(sWord);
            fs.Close();
        }
    }
}